/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is iRecipe code.
 *
 * The Initial Developer of the Original Code is Robert Morris.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

package com.pavisoft.irecipe;
import javax.swing.JLabel;
import org.jdom.*;

/**
 * @author Robert Morris
 *
 * An <code>Ingredient</code> is responsbible for representing the amount of,
 * name of, and any other information about an ingredient in a recipe.
 *
 */
public class Ingredient implements Cloneable
{   
    private String singularName;  //e.g., pepper
    private String pluralName;    //e.g., peppers
    private Amount amount;   //e.g., 2 tsp (composed of Quantity and unit)
    private String additionalInfo; //e.g., "chopped"
    private boolean countInNutritionalData = false; // for future use
    
    /**
     * Creates a new instance of Ingredient with specified name, amount,
     *  and units.
     */
    public Ingredient(String name, Amount amount)
    {
        this(name, name, amount);
    }
    
    /**
     * Creates a new instance of Ingredient with specified name (singular and plural), amount,
     *  and units.
     */
    public Ingredient(String singularName, String pluralName, Amount amount)
    {
        this.singularName = singularName;
        this.pluralName = pluralName;
        this.amount = amount;
    }
    
    public Ingredient(String name)
    {
        this(name, new Amount());
    }
    
    /**
     * Tries to parse an ingredient into quantity, units, and ingredient name.
     *  
     *  @param stringToParse The string to parse
     *  @param returnOriginalStringIfFail If <code>true</code>, will return an <code>Ingredient</code>
     *   whose name is equal to <code>stringToParse</code> if this method could not parse the string
     *   into an <code>Ingredient</code>. If <code>false</code>, method will
     *   return <code>null</code> if the string could not be usefully parsed.
     *  @return The parsed <code>Ingredient</code> (see above for more details).
     */

    public static Ingredient parseIngredient(String stringToParse, boolean returnOriginalStringIfFail)
    {
        Ingredient ingredient = null;
        Amount amount = null;
        Quantity quantity = null;
        Amount.Unit unit = null;
        
        // Try to eliminate extra spaces from string
        stringToParse = stringToParse.replace("   ", " ");
        stringToParse = stringToParse.replace("  ", " ");
        stringToParse = stringToParse.trim();
        
        // Split into space-delimited components
        String[] components = stringToParse.split(" ");
        
        if (components.length >= 5)
        {
            // Anything beyoned 4th part is probably part of ingredient,
            // so just consolidate "extra" components into one.
            for (int i = 4; i < components.length; i++)
            {
                components[3] += " " + components[i];
            }
            components =
                    new String[] { components[0], components[1], components[2],
                                    components[3] };
        }
        
        boolean hasFirstComponent = (components.length >= 1);
        boolean hasSecondComponent = (components.length >= 2);
        boolean hasThirdComponent = (components.length >= 3);
        boolean hasFourthComponent = (components.length >= 4);
        
        if (hasFirstComponent)
        {
            // Work with first component. Should be number.
            quantity = Quantity.parseQuantity(components[0]);
            if (quantity == null)
            {
                // not a number ... so can't do anything (here or w/ rest of string)
            }
            else
            {
                if (hasSecondComponent)
                {
                    // Try second component. Three possibilities:
                    //
                    // (a) Could be fractional component of number (e.g., the "2/3"
                    //     of "1 2/3 cup flour");
                    //
                    // (b) Could be unit (e.g., the "tsp" in "1 tsp sugar");
                    //     
                    // (c) Might be the ingredient in a unitless situation, such
                    //     the "tomato" in "1 tomato".

                    // Check for possibility A:
                    Quantity fraction = Quantity.parseQuantity(components[1]);
                    if (fraction != null)
                    {
                        // Possibility A has been encountered.
                        quantity = Quantity.add(quantity, fraction);                    
                    }
                    else
                    {
                        // Not possibility A, but could be possibility B or C
                        // Check for possibility B:
                        unit = Amount.parseUnit(components[1]);
                        if (unit == Amount.Unit.OTHER)
                        {
                            // Not B, so assume C
                            // Put all parts but first (number) back together:
                            String ingName = components[1];
                            for (int i = 2; i < components.length; i++)
                            {
                                ingName += " " + components[i];
                            }
                            unit = Amount.Unit.NO_UNIT;
                            amount = new Amount(quantity, unit);
                            ingredient = new Ingredient(ingName, amount);
                        }
                        else
                        {
                            // Unit found, so is Possibility B
                            amount = new Amount(quantity, unit);
                        }
                    }
                    
                    if (hasThirdComponent && ingredient == null)
                    {
                        // Try to determine meaning of third component. Two
                        // possibilities left:
                        // (1) Could be unit (e.g., "cup" in "1 2/3 cup flour")
                        // (2) Could be ingredient (e.g., "milk" in "1 cup milk")
                        if (unit == null)
                        {
                            unit = Amount.parseUnit(components[2]);
                            if (unit != Amount.Unit.OTHER)
                            {
                                // Possibility 1 encountered (is unit)
                                amount = new Amount(quantity, unit);
                            }
                            else
                            {
                                // Assume Possibility 2
                                // (Would this code ever be reached?! Or would
                                // it always go to the one below...oh well,
                                // leave this here for now...)
                                unit = Amount.Unit.NO_UNIT;
                                String ingName = components[2];
                                // Put all parts after third back together to
                                // form ingredient name:
                                for (int i = 3; i < components.length; i++)
                                {
                                    ingName += " " + components[i];
                                }
                                amount = new Amount(quantity, unit);
                                ingredient = new Ingredient(ingName, amount);
                            }
                        }
                        else
                        {
                            // Already found unit (because != null), so assue
                            // that this third compontent is ingredient, as in
                            // Possibility 2
                            String ingName = components[2];
                            // Put all parts after third back together to
                            // form ingredient name:
                            for (int i = 3; i < components.length; i++)
                            {
                                ingName += " " + components[i];
                            }
                            amount = new Amount(quantity, unit);
                            ingredient = new Ingredient(ingName, amount);
                        }
                        
                        if (hasFourthComponent && ingredient == null)
                        {
                            // Assume fourth compontent is ingredient
                            // double-check....
                            if (unit == null)
                                unit = Amount.Unit.NO_UNIT;
                            if (amount == null)
                                amount = new Amount(quantity, unit);
                            ingredient = new Ingredient(components[3], amount);
                        }
                    }
                }
                else
                {
                    // No second component...just number. What?!
                    // Set quantity to null so bottom 'else' thinks
                    // nothing happened (since we really didn't discover anything)
                    quantity = null;
                }
            }
        }
        else
        {
            // part does not have a space in it...perhaps something like "peppers"?
            // We'll take care of this (and any other non-parseable situation)
            // at the end of this method.
        }
        
        if ((ingredient == null) && (returnOriginalStringIfFail))
        {
            ingredient = new Ingredient(stringToParse);
        }

        return ingredient;
    }
              
    
    /** Sets additional information about this ingredient, e.g., "frozen" or
     *  "chopped". */
    public void setAdditionalInfo(String info)
    {
        this.additionalInfo = info;
    }
    
    /** Returns additional information (if any--a check against <code>null</code>
     *  should be made by code that depends on the return value of this method)
     *  associated with this ingredient, e.g., "chopped" or "frozen".
     */
    public String getAdditionalInfo()
    {
        return this.additionalInfo;
    }

    public String getSingularName()
    {
        return this.singularName;
    }

    public void setSingularName(String name)
    {
        this.singularName = name;
    }

    public Amount getAmount()
    {
        return this.amount;
    }

    public void setAmount(Amount amount)
    {
        this.amount = amount;
    }

    public String getPluralName()
    {
        return pluralName;
    }

    public void setPluralName(String pluralName)
    {
        this.pluralName = pluralName;
    }
    
    /**
     * Converts this <code>Ingredient</code> into a human-friendly string
     *  that conveys the necessary information, e.g., "2 c. milk".
     */
    public String toString()
    {
        String s = "";      
        if (this.amount.getUnit() == Amount.Unit.NO_UNIT && this.amount.getQuantity().equals(1))
        {
            // if quantiy has no units and amount is 1, use singular ingredient name...
            s += this.amount.toString() + " " + this.getSingularName();
        }
        else
        {
            // ... otherwise use plural
            s += this.amount.toString();
            if (!s.equals(""))
            {
                s += " ";
            }
            s += this.getPluralName();
        }
        
        if (this.additionalInfo != null)
        {
            s += ", " + additionalInfo;
        }
        
        return s;
    }

    public Object clone()
    {
        return new Ingredient(new String(this.singularName),
                              new String(this.pluralName),
                              (Amount)this.amount.clone());
    }
    
    /** Returns an <code>org.jdom.Element</code> representing this <code>Ingredient</code>
     */
    public Element toXML()
    {
       Element ingElem = new Element("ing");
       Element amtElem = new Element("amt");
       Element qtyElem = new Element("qty");
       Element unitElem = new Element("unit");       
       Element itemElem = new Element("item");     
       
       itemElem.addContent(this.getSingularName());
       
       if (this.amount.getUnit() != Amount.Unit.NO_UNIT)
       {
           unitElem.addContent(this.amount.getUnit().getSingularName());
       }
       
       qtyElem.addContent(this.amount.getQuantity().toString());
       
       amtElem.addContent(qtyElem);
       if (!(unitElem.getContent().isEmpty()))
       {
           amtElem.addContent(unitElem);
       }
       
       ingElem.addContent(amtElem);
       ingElem.addContent(itemElem);
       
       return ingElem;     
    }
}
